#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<semaphore.h>
#include<fcntl.h>
#include <sys/types.h>  
#include <sys/wait.h>
#define BUF_MAX_SIZE 8192

sem_t *w, *r ,*start;//设置信号量

int main(){
    /*
    管道只能用于具有亲缘关系的进程，可以将其看作一个文件，但有别于普通的文件， 管道一次只可以被一个进程访问，能实现互斥；
    pipe(int fd[] ), 其参数为长度为2的int数组，分别代表读端fd[0], 写端fd[1], 在创建管道后，f d[0],fd[1]成为文件描述符；
    写入（write）管道一端fd[1]的数据，在管道的另一端fd[0]可以被进程读取(read)；
    */

    int filedis[2];//0为读口，1为写口
    pipe(filedis); //开启管道通信
    char buf[256];
    char pid4_buf[8192];
    char *name1 = "writer";
    char *name2 = "reader";
    int x, y;
    int flag = 0;
    /**
    * parameter1:  name1:   写信号量的名称
    * parameter2:  name2:   读信号量的名称
    * parameter3:  O_CREAT: 创建一个信号量
    * parameter:   0666：   信号量的读取权限
    * parameter:   0：      信号量的初始值
    */
    w = sem_open(name1, O_CREAT, 0666, 0);//写信号
    r = sem_open(name2, O_CREAT, 0666, 0);//读信号
    start = sem_open("start", O_CREAT, 0666, 0);//pid4开始执行的信号

    /**
    * @brief 读取当前w信号量的值，如果为1则++
    * 
    */
    sem_getvalue(w, &x);
    if(x == 0) {
        sem_post(w); //释放资源操作，w信号量的值+1
    }

    pid_t pid1, pid2, pid3, pid4;//创建进程
    pid1 = 1;
    pid2 = 1;  
    pid3 = 1;
    //pid4 = 1;
    pid1 = fork();             //开启第一个子进程
    if(pid1 > 0){            //开启三个子进程的父进程
        pid2 = fork();           //开启第二个子进程
    }
    if(pid1 > 0 && pid2 > 0){//开启三个子进程的父进程
        pid3 = fork();             //开启第三个子进程
    }
    if(pid1 > 0 && pid2 > 0 && pid3 > 0){
        pid4 = fork();        //开启第四个子进程
    }

    if(pid1 == 0) {   // 第一个子进程需要进行的操作
        close(filedis[0]);//读口关闭
        sem_wait(w); //请求共享空间
        printf("child process1 will send message!\n");
        printf("child1:Please input what message you want to send:");
        char message[100];
        fgets(message, sizeof(message), stdin);
        write(filedis[1],message,strlen(message));//写口输入
        sem_post(w);  //释放共享空间
        sem_post(r);  //告知父进程可以读
        sem_post(start);
        exit(0);      //推出子程序
    }
    if(pid2 == 0) {   // 第二个子进程需要进行的操作
        close(filedis[0]); 
        sem_wait(w);
        printf("child process2 will send message!\n");
        printf("child2:Please input what message you want to send:");
        char message[100];
        fgets(message, sizeof(message), stdin);
        write(filedis[1],message,strlen(message));//写口输入
        sem_post(w);
        sem_post(r);
        sem_post(start);
        exit(0);
    }
    if(pid3 == 0) {    // 第三个子进程需要进行的操作
        close(filedis[0]);
        sem_wait(w);
        printf("child process3 will send message!\n");
        printf("child3:Please input what message you want to send:");
        char message[100];
        fgets(message, sizeof(message), stdin);
        write(filedis[1],message,strlen(message));//写口输入
        sem_post(w);
        sem_post(r);
        sem_post(start);
        exit(0);
    }
    if (pid4 == 0){
        sem_wait(start);
        sem_wait(start);
        sem_wait(start);
        sem_wait(start);
        sem_wait(w);
        puts("start!");
        int pipefd[2];
        int flag = 0;
        int n;
        char buf[BUF_MAX_SIZE];
        memset(buf, 0, BUF_MAX_SIZE);
        pipe(pipefd);
        int count = 0;
        //close(pipefd[0]);
        int flags = fcntl(pipefd[1], F_GETFL);
    // 管道默认是阻塞写，通过`fcntl`设置成非阻塞写，在管道满无法继续写入时返回-EAGAIN，作为循环终止条件
        fcntl(pipefd[1], F_SETFL, flags | O_NONBLOCK);
        // 写入管道
        while (!flag)
        {
            n = write(pipefd[1], buf, 1);
            if (n == -1)
            {
                flag = 1;
            }
            else
            {
                count++;
            }
        }
        int space = count / 1024;
        //printf("space = %dKB\n", count/ 1024);
        char tmp[10];
        sprintf(tmp, "%d", space);
        char res[100];
        strcat(res, "space = ");
        strcat(res, tmp);
        strcat(res, "KB");
        puts("end!");
        puts(res);
        sem_post(w);
        exit(0);
    }

    if(pid1 > 0 && pid2 > 0 && pid3 > 0){    // 父进程需要进行的操作
        sem_wait(r);  // 等待第一个可读信号
        sem_wait(r);  // 等待第二个可读信号
        sem_wait(r);  // 等待第三个可读信号
        sem_wait(w);  // 等待一个可写信号
        printf("3 child process finish sending, the messages are:");
        close(filedis[1]);
        read(filedis[0], buf, sizeof(buf));
        printf("\n%s",buf);
        sem_post(w);
        sem_post(start);
        //waitpid(pid4, NULL, 0);
        exit(0);
    }
    return 0;
}